'use strict';
import {apply,isObject,extend} from '../base/base';
import req from './req';

import {querystring} from './querystring';

//默认响应处理 handler
import defaultResponseHandler from './defaultResponseHandler';

/**
 * 拼接 url，把 url1 和 url2 拼接为一个 url
 *
 * 拼接后的url结果为以 https:// 或 http:// 或 / 或 // 打头的 url
 * 以 / 打头的 url 以 `{scheme}://{host}:{port}{url}` 形式生成最终请求 url
 * 以 // 打着呢的 url 以 `{scheme}:` 形式生成最终请求 url
 */
var urlConFun = (preUrl = '') => {

    //如果 pre 以 / 结尾且长度大于1，则去掉最后面的 /
    if (preUrl.charAt(preUrl.length - 1) == '/' && preUrl.length > 1) {
        preUrl = preUrl.substring(0, preUrl.length - 1);
    }

    //如果 pre 不以 http:// 和 https:// 和 / 打头的字符串
    //则在最前面加一个 /
    if (!(preUrl.toLowerCase().startsWith('http://')
      || preUrl.toLowerCase().startsWith('https://')
        || (preUrl.startsWith('/') && preUrl.length > 1))) {
        preUrl = '/' + preUrl;
    }

    //如果 pre == '/'，pre设置空字符串，在后面拼接时始终会在中间加一个 /
    // 如果不去掉，最后的 url 会以 // 开头
    if(preUrl == '/'){
        preUrl = '';
    }

    return (url) => {
        //如果以 https:// 、 https:// 、 // 打头，则不处理
        if (url && url.toLowerCase && url.toLowerCase().startsWith('http://')
          || url.toLowerCase().startsWith('https://')
          || url.startsWith('//')) {
            return url;
        }
        if (url && url.charAt && url.charAt(0) == "/") {
            return preUrl + url;
        } else {
            return preUrl + "/" + url;
        }
    }
}

/*
 http 请求类，完成 http 请求，默认采用 fetch 方式完成请求，在不支持 fetch 进采用 ajax 方式完成请求

*/
class Http{

    constructor(){
        //默认为'/'
        this._httpCommUrlFun = urlConFun();
        //默认请求头
        this._defaultHeaders = {}
        //认证信息
        this._auth = undefined;
        //是否起用 cache
        this.cache = false;
    }

    /**
     * 设置通信 url，支持'http://...' 、 'https://...'、'str'、function(){...} 四种参数
     * 若以'http://...' 、 'https://...'开头，则把请求时的 url 到此 url 后面
     * 若不是'http://...' 、 'https://...'开头的字符串，则组装为 / 开头的路径
     * 若是 function 类型参数，则把请求时 url 给函数处理，返回处理后的 url
     * 默认值为 /
     */
    commUrl(url = '/') {
        if(typeof url == 'string') {
            this._httpCommUrlFun = urlConFun(url)
        }else if(typeof url == 'function'){
            // fetch请求时，基于当前面页面路径的相对路径，一般无实际意义，反而增的复杂度
            // 如：当前 http://a.com/b/c/b，则  ../e，实际为 http://a.com/b/c/e
            // 当函数返回结果不是以 http:// 、 https:// 、 // 、 / 四个其中一个做为开着的url返回值时，在url前面强制加上 '/'
            this._httpCommUrlFun = ( fn => {
                let conFun = urlConFun();
                //此处不能用 => 函数，在转码时，外部会有一个 arguments 变量，会导致 => 拿不到真实的arguments
                return function () {
                    //在不是以 http:// 、 https:// 、 // 、 / 打头时，在url前面强制加上 '/'
                    return conFun(fn.apply(this,arguments))
                }
            })(url);
        }else{
            //如果设置的其他值，则默认处理
            this._httpCommUrlFun = urlConFun();
        }
    }

    /**
     * 添加默认请求头，在所有的请求中都加入这个请求头信息
     * @param header
     * @param value
     */
    addHeader(header, value) {
        this._defaultHeaders[header] = value;
    }

    /**
     * 认证信息，在http请求时，生成认证信息添加在 Authorization 请求头中
     * @param config
     */
    auth(config) {
        this._auth = config;
    }

    //判断当前请求是否为文件上传
    _isupload(config){
        var params = config.params || {};
        //表示是否为文件上传
        if ((config.contentType || '').startsWith('multipart/form-data')) {
            return true;
        } else {
            for (var param in params) {
                if (params[param] && params[param].constructor && params[param].constructor.name == 'File') {
                    return true;
                }
            }
        }
        return false;
    }

    _setFileuploadBody(config){
        config.method = 'post';
        var params = config.params || {};
        var httpParams = new FormData();
        for (var param in params) {
            httpParams.append(param, params[param]);
        }
        //config.body 中的值优先
        config.body = config.body || httpParams;
    }

    _setBodyParams(config){

        var params = config.params || {};

        if(config.contentType == 'application/json'){
            //如果是 application/json 类型参数，则把 params 转为 json 字符串
            //config.body 优先
            config.body = config.body || JSON.stringify(params);
        }else{
            //构建参数请求 body
            var httpBody = querystring.encode(params);
            if (config.method.toLowerCase() !== "post") {
                //如果不是 post 请求，把请求参数添加到请求 url 中
                if (config.url.indexOf('?') != -1) {
                    config.url += '&' + httpBody;
                } else {
                    config.url += '?' + httpBody;
                }
            }else{
                //如果是 post 请求，则放到 body 中
                //config.body 中的值优先
                httpBody = querystring.encode(params);
                config.body = config.body || httpBody;
            }
        }
    }

    //组装请求参数
    _ajaxParams(config) {


        //如果参数为字符串
        if (typeof config.params === 'string') {
            config.body = config.body || config.params;
            //把 params 置空
            config.params = undefined;
        }

        //表示是否为文件上传
        var isupload = this._isupload(config);
        if(isupload){
            //如果是文件上传，强制转为 post 请求
            config.method = 'post';
        }

        if(!config.contentType && config.method.toLowerCase() == "post"){
            //如果没有设置 contentType ，且是 post 请求时，设置默认的 contentType
            config.contentType = 'application/x-www-form-urlencoded';
        }

        //如果为文件上传，采用 FormData 方式上传数据
        if(isupload){
            //文件上传时，不能设置 Content-Type
            config.contentType = '';
            this._setFileuploadBody(config);
        } else {
            this._setBodyParams(config);
        }

        //处理缓存信息
        if(!this.cache){
            config.url += (config.url.indexOf('?') === -1 ? '?' : '&') + new Date().getTime() + Math.random() + '=1'
        }
        config.url = this._httpCommUrlFun(config.url);

    }

    //处理配置信息，包括请求头，认证信息，并增加 ajax 请求标识
    async _processConfig(config){

        config.method = config.method || 'post';

        this._ajaxParams(config);

        var headers = config.headers || {};

        var allHeaders = {
            //表示是 ajax 请求
            'X-Requested-With' : 'XMLHttpRequest',
        };

        for (var header in this._defaultHeaders) {
            allHeaders[header] = this._defaultHeaders[header];
        }
        for (var header in headers) {
            allHeaders[header] = headers[header];
        }
        // 添加认证信息请求头
        if(this._auth && this._auth.getAuth && typeof this._auth.getAuth == 'function') {
            allHeaders["Authorization"] = await this._auth.getAuth();
        }
        config.headers = allHeaders;

        if (config.async === undefined || config.async === null) {
            config.async = true;
        }
    }

    async http(url,config) {

        try {
            //http请求前，先执行 request 回调
            (this._callback && this._callback.request || function () {})(url, config);
        } catch (e) {
            //防止回调出错导致 http 请求中断
        }

        return this._http(url, config).then((data) => {
            try {
                //执行请求成功回调
                (this._callback && this._callback.complete || function () {})(data, url, config);
            } catch (e) {
                //防止回调出错导致 http 请求中断
            }
            return data;
        }, (error) => {
            try {
                //失败回调
                (this._callback && this._callback.fault || function () {})(error, url, config);
            } catch (e) {
                //防止回调出错导致 http 请求中断
            }
            //抛出错误
            throw error;
        })
    }

    async _http(url,config){
        if (isObject(url)) {
            config = extend(url);
        } else {
            config = extend(config);
            config.url = url;
        }

        //处理请求参数
        await this._processConfig(config);


        // && 条件的顺序不能错
        //对 config 配置进行拦截
        (typeof config.requestHandle == 'function' && config.requestHandle
            || (function(){}))(config);

        //发起 http 请求
        var response = await req(config);

        //处理返回结果
        var result = (typeof config.responseHandler == 'function' && config.responseHandler
            || defaultResponseHandler)(response,config);
        if(result instanceof Promise){
            //如果是异步操作
            return await result;
        }
        return result;
    }

    // http.void request(HttpCommImpl impl, HttpPackage pack);

    // void requestComplete(HttpCommImpl impl, HttpPackage pack, Object obj, List<Error> warning);

    // void requestFault(HttpCommImpl impl, HttpPackage pack, Error error);
    /**
     * 回调函数设置
     */
    callback(callback){
        this._callback = callback;
    }
}

//存储 http 请求对象
//多 http 请求对象在浏览器一般无意义，在 react native 下，要请求多个服务时才需要
var _httpFuns = {};

//js 是单线程，不用考虑线程同步问题
export function getHttp(name){

    if(_httpFuns[name]){
        return _httpFuns[name];
    }

    var httpObj = new Http();

    var httpFun = async function(url,config){
        return httpObj.http(url,config);
    }

    //给http加上全局方法
    httpFun.addHeader = httpObj.addHeader.bind(httpObj);
    httpFun.commUrl = httpObj.commUrl.bind(httpObj);
    httpFun.auth = httpObj.auth.bind(httpObj);
    httpFun.callback = httpObj.callback.bind(httpObj);

    Object.defineProperty(httpFun, "cache", {
        get:function(){
            return httpObj.cache;
        },
        set:function(v){
            httpObj.cache = v;
        }
    });

    _httpFuns[name] = httpFun;

    return httpFun;
}

//导出 http 请求
export var http = getHttp("global");

//http.build().header().param().require();


